/**
 * Copyright (c) [2021-2021] [libbylg@126.com]
 * [lplogger] is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL
 * v2. You may obtain a copy of Mulan PSL v2 at:
 *             http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more
 * details.
 */
#include "lp_logger.h"
#include "lp_asserts.h"

#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

// clang-format off
static LP_SINK_PROVIDER     lp_sink_provider = NULL;
static int8_t               lp_sinks_count = 0;
static LP_FORMAT_PROVIDER   lp_format_provider = NULL;
static char                 lp_format_escape_char = '%';
static LP_MODULE_PROVIDER   lp_module_provider = NULL;
static int8_t               lp_modules_count = 0;
static int8_t               lp_module_level[LP_MODULE_COUNT_MAX] = {0};
static LP_MSGVIEW_PROVIDER  lp_getter_provider = NULL;
static LP_LEVEL_PROVIDER    lp_level_getter = NULL;
static int8_t               lp_levels_count = 0;
static int8_t               lp_global_level = LP_LEVEL_MIN;
// clang-format on

LPEXTERN int lp_define_sinks(int8_t count, LP_SINK_PROVIDER provider) {
    if ((count <= 0) || (count > LP_SINK_COUNT_MAX)) {
        ASSERT(false);
        return -1;
    }

    if (provider == NULL) {
        ASSERT(false);
        return -1;
    }

    lp_sink_provider = provider;
    lp_sinks_count = count;

    return 0;
}

LPEXTERN const struct lp_sink_t* lp_get_sink(int8_t sink_id) {
    if ((sink_id < 0) || (sink_id >= lp_sinks_count)) {
        ASSERT(false);
        return NULL;
    }

    return lp_sink_provider(sink_id);
}

LPEXTERN int8_t lp_get_sinks_count() {
    return lp_sinks_count;
}

LPEXTERN int lp_setup_sink(int8_t sink_id, int8_t attr_id, uintptr_t value) {
    if ((sink_id < 0) || (sink_id >= lp_sinks_count)) {
        ASSERT(false);
        return -1;
    }

    if ((attr_id < 0) || ((int)(attr_id) >= LP_SINK_ATTR_COUNT_MAX)) {
        ASSERT(false);
        return -1;
    }

    if (lp_sink_provider == NULL) {
        ASSERT(false);
        return -1;
    }

    struct lp_sink_t* sink = lp_sink_provider(sink_id);
    if (sink == NULL) {
        ASSERT(false);
        return -1;
    }

    sink->setup(sink, attr_id, value);

    return 0;
}

LPEXTERN void lp_clear_sinks() {
    lp_sinks_count = 0;
}

LPEXTERN int lp_define_formats(char escape_char, LP_FORMAT_PROVIDER provider) {
    if (provider == NULL) {
        return -1;
    }

    //  必须是符号字符
    if (!ispunct(escape_char)) {
        return -1;
    }

    lp_format_provider = provider;
    lp_format_escape_char = escape_char;
    return 0;
}

LPEXTERN struct lp_format_t* lp_get_format(char keychar) {
    //  如果 keychar 超出范围
    if (!isgraph(keychar)) {
        return NULL;
    }

    //  转义字符自身是不能作为 format 的关键字符的
    if (keychar == lp_format_escape_char) {
        return NULL;
    }

    if (lp_format_provider == NULL) {
        ASSERT(false);
        return NULL;
    }

    return lp_format_provider(keychar);
}

LPEXTERN char lp_get_format_escape_char() {
    return lp_format_escape_char;
}

LPEXTERN int lp_parse_pattern(const char* pattern, void* context, LP_PARSE_PATTERN_ENUMERATOR callback) {
    if (pattern == NULL) {
        ASSERT(false);
        return -1;
    }

    if (callback == NULL) {
        ASSERT(false);
        return -1;
    }

    //  必须是符号字符
    if (!ispunct(lp_format_escape_char)) {
        ASSERT(false);
        return -1;
    }

    char buff[LP_PATTERN_LEN_MAX] = { 0 };
    char* text = buff;

    for (const char* ptr = pattern; (*ptr != '\0') && ((ptr - pattern) < LP_PATTERN_LEN_MAX); ptr++) {
        //  如果遇到的不是转义字符，那么直接拷贝到 buff 中去备用
        if (*ptr != lp_format_escape_char) {
            *(text++) = *ptr;
            continue;
        }

        //  如果没有后续字符了，那么只能当做原始符号处理
        if (*(ptr + 1) == '\0') {
            *(text++) = *ptr;
            break;
        }

        ptr++;

        //  如果刚好遇到转义字符自身，说明是对自身的转义
        if (*ptr == lp_format_escape_char) {
            *(text++) = *ptr;
            continue;
        }

        //  不能是不可见字符
        if (!isgraph(*ptr)) {
            *(text++) = lp_format_escape_char;
            *(text++) = *ptr;
        }

        //  如果 buff 中还有内容，那么先处理非转义部分
        if (text != buff) {
            *text = '\0';
            callback(context, LP_PATTERN_PLAINTEXT, buff, text - buff);

            //  重置 text
            text = buff;
        }

        buff[0] = *ptr;
        buff[1] = '\0';
        callback(context, LP_PATTERN_KEYCHAR, buff, 1);
    }

    //  收尾
    if (text != buff) {
        *text = '\0';
        callback(context, LP_PATTERN_PLAINTEXT, buff, text - buff);
    }

    return 0;
}

static int lp_compile_pattern_as_formatlist_callback(void* context, int8_t type, const char* text, int8_t text_len) {
    ASSERT(context != NULL);
    ASSERT(text != NULL);
    ASSERT(text_len > 0);

    struct lp_formatlist_t* formatlist = (struct lp_formatlist_t*)(context);

    if (type == LP_PATTERN_PLAINTEXT) {
        lp_formatlist_add_tail(formatlist, lp_new_format_plaintext(text, text_len));
    } else {
        lp_formatlist_add_tail(formatlist, lp_get_format(text[0]));
    }

    //! 最后追加个换行
    lp_formatlist_add_tail(formatlist, lp_new_format_linebreak());
    return 0;
}

LPEXTERN int lp_compile_pattern(const char* pattern, struct lp_formatlist_t* formatlist) {
    return lp_parse_pattern(pattern, formatlist, lp_compile_pattern_as_formatlist_callback);
}

LPEXTERN void lp_clear_formats() {
    lp_format_provider = NULL;
}

LPEXTERN int lp_define_modules(int8_t count, LP_MODULE_PROVIDER provider) {
    if ((count <= 0) || (count > LP_MODULE_COUNT_MAX)) {
        ASSERT(false);
        return -1;
    }

    if (provider == NULL) {
        ASSERT(false);
        return -1;
    }

    for (int i = 0; i < count; i++) {
        const struct lp_module_t* model = provider((int8_t)(i));
        if (model == NULL) {
            return -1;
        }

        lp_module_level[i] = model->deflevel;
    }

    lp_modules_count = count;
    return 0;
}

LPEXTERN const struct lp_module_t* lp_get_module(int8_t module_id) {
    if ((module_id < 0) || (module_id >= lp_modules_count)) {
        return NULL;
    }

    return lp_module_provider(module_id);
}

LPEXTERN int8_t lp_get_modules_count() {
    return lp_modules_count;
}

LPEXTERN void lp_clear_modules() {
    lp_module_provider = NULL;
    lp_modules_count = 0;
}

LPEXTERN int lp_define_levels(int8_t count, LP_LEVEL_PROVIDER provider) {
    if ((count <= 0) || (count > LP_LEVEL_COUNT_MAX)) {
        ASSERT(false);
        return -1;
    }

    if (provider == NULL) {
        ASSERT(false);
        return -1;
    }

    lp_level_getter = provider;
    lp_levels_count = count;
    return 0;
}

LPEXTERN struct lp_level_t* lp_get_level(int8_t level) {
    if ((level < LP_LEVEL_MIN) || (level >= lp_levels_count)) {
        ASSERT(false);
        return NULL;
    }

    if (lp_level_getter == NULL) {
        return NULL;
    }

    return lp_level_getter(level);
}

LPEXTERN int8_t lp_get_levels_count() {
    return lp_levels_count;
}

LPEXTERN void lp_clear_levels() {
    lp_level_getter = NULL;
    lp_levels_count = 0;
}

LPEXTERN int lp_define_msgview(LP_MSGVIEW_PROVIDER provider) {
    if (provider == NULL) {
        return -1;
    }

    lp_getter_provider = provider;
    return 0;
}

LPEXTERN uintptr_t lp_get_msgview(void* msg, int8_t msgview_id) {
    if (lp_getter_provider == NULL) {
        return (uintptr_t)(NULL);
    }

    return lp_getter_provider(msg, msgview_id);
}

LPEXTERN void lp_log_print(uint64_t sink_mask, int8_t module_id, int8_t level, void* msg) {
    if (sink_mask == 0) {
        ASSERT(false);
        return;
    }

    //  模块编号超出范围
    if (module_id >= lp_modules_count) {
        ASSERT(false);
        return;
    }

    //  日志级别超出范围: 这种情况是允许的
    if (level >= lp_levels_count) {
        return;
    }

    if (level < LP_LEVEL_MIN) {
        ASSERT(false);
        return;
    }

    if (level > lp_levels_count) {
        return;
    }

    if (level < lp_module_level[module_id]) {
        return;
    }

    if (level < lp_global_level) {
        return;
    }

    if (lp_sink_provider == NULL) {
        ASSERT(false);
        return;
    }

    //  所有限制条件都通过了，准备输出
    for (uint64_t index = 0; index < lp_sinks_count; index++) {
        if (sink_mask & ((uint64_t)(1) << index)) {
            struct lp_sink_t* sink = lp_sink_provider(index);
            ASSERT(sink != NULL);
            sink->print(sink, msg);
        }
    }
}

LPEXTERN int8_t lp_get_module_level(int8_t module_id) {
    if ((module_id < 0) || (module_id >= lp_modules_count)) {
        ASSERT(false);
        return -1;
    }
    return lp_module_level[module_id];
}

LPEXTERN int lp_set_module_level(int8_t module_id, int8_t level) {
    if ((module_id < 0) || (module_id >= lp_modules_count)) {
        ASSERT(false);
        return -1;
    }

    //  日志级别可以等于 lp_levels_count，这表示关闭所有日志
    if ((level < LP_LEVEL_MIN) || (level > lp_levels_count)) {
        ASSERT(false);
        return -1;
    }

    lp_module_level[module_id] = level;
    return 0;
}

LPEXTERN int lp_set_modules_level(uint64_t modules_mask, int8_t level) {
    //  日志级别可以等于 lp_levels_count，这表示关闭所有日志
    if ((level < LP_LEVEL_MIN) || (level > lp_levels_count)) {
        ASSERT(false);
        return -1;
    }

    if (modules_mask == 0) {
        ASSERT(false);
        return -1;
    }

    for (int module_id = 0; module_id < lp_modules_count; module_id++) {
        if (((uint64_t)(1) << module_id) & modules_mask) {
            lp_module_level[module_id] = level;
        }
    }

    return 0;
}

LPEXTERN int lp_set_global_level(int8_t level) {
    //  日志级别可以等于 lp_levels_count，这表示关闭所有日志
    if ((level < LP_LEVEL_MIN) || (level > lp_levels_count)) {
        ASSERT(false);
        return -1;
    }

    lp_global_level = level;
    return 0;
}

LPEXTERN int8_t lp_get_global_level() {
    return lp_global_level;
}

//LPEXTERN bool lp_is_level_printable(int8_t module_id, int8_t level) {
//    if (level >= lp_get_module_level(module_id)) {
//        if (level >= lp_get_global_level()) {
//            return true;
//        }
//    }
//
//    return false;
//}

LPEXTERN int32_t lp_format_linebreak(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

#if defined(WIN32)
    const char* line_break = "\r\n";
    int32_t line_break_len = 2;
    if (*len < line_break_len) {
        return line_break_len;
    }
    (*ptr)[0] = line_break[0];
    (*ptr)[1] = line_break[1];
    return 0;
#else
    const char* line_break = "\n";
    int32_t line_break_len = 1;
    if (*len < line_break_len) {
        return line_break_len;
    }
    (*ptr)[0] = line_break[0];
    return 0;
#endif
}

struct lp_format_plaintext_t {
    struct lp_format_t parent;
    int32_t plaintext_len;
    char plaintext[0];
};

LPEXTERN int32_t lp_format_plaintext(struct lp_format_t* format, void* msg, char** ptr, int32_t* len) {
    ASSERT(msg != NULL);
    ASSERT(ptr != NULL);
    ASSERT(len != NULL);
    ASSERT(*ptr != NULL);

    ASSERT(format != NULL);
    struct lp_format_plaintext_t* plaintext_format = (struct lp_format_plaintext_t*)(format);
    ASSERT(plaintext_format->plaintext_len > 0);
    ASSERT(plaintext_format->plaintext != NULL);

    if (*len < plaintext_format->plaintext_len) {
        return plaintext_format->plaintext_len;
    }

    memcpy(*ptr, plaintext_format->plaintext, plaintext_format->plaintext_len);
    *ptr += plaintext_format->plaintext_len;
    *len -= plaintext_format->plaintext_len;

    return 0;
}

LPEXTERN struct lp_format_t* lp_new_format_linebreak() {
    struct lp_format_t* linebreak_format = (struct lp_format_t*)malloc(sizeof(struct lp_format_t));
    ASSERT(linebreak_format != NULL);
    linebreak_format->format = lp_format_linebreak;
    return linebreak_format;
}

LPEXTERN struct lp_format_t* lp_new_format_plaintext(const char* plaintext, int32_t len) {
    ASSERT(plaintext != NULL);
    ASSERT(len >= 0);

    struct lp_format_plaintext_t* plaintext_format = (struct lp_format_plaintext_t*)malloc(sizeof(struct lp_format_plaintext_t) + len);
    ASSERT(plaintext_format != NULL);
    plaintext_format->parent.format = lp_format_plaintext;
    plaintext_format->plaintext_len = len;
    memcpy(plaintext_format->plaintext, plaintext, len);

    return (struct lp_format_t*)(plaintext_format);
}

LPEXTERN void lp_new_format(struct lp_format_t* format) {
    if (format == NULL) {
        return;
    }

    free(format);
}

struct lp_format_context_t {
    struct lp_buffer_t* buffer;
    void* msg;
    int32_t result;
};

static int lp_formatlist_format(void* context, struct lp_format_t* format) {
    ASSERT(context != NULL);
    ASSERT(format != NULL);

    struct lp_format_context_t* format_context = (struct lp_format_context_t*)(context);

    struct lp_buffer_t* buffer = format_context->buffer;
    struct lp_buffer_t* msg = format_context->msg;

    char* ptr = buffer->data + buffer->len;
    int32_t len = buffer->cap - buffer->len;
    int32_t expect_len = (format->format)(format, msg, &ptr, &len);

    if (expect_len == 0) {
        buffer->len = buffer->cap - len;
        format_context->result = 0;
        return 0;
    }

    //  如果还需要更多缓冲区就重新分配内存，然后重试一次
    if (expect_len > 0) {
        int ret = lp_reserve_buffer(buffer, buffer->cap + expect_len);
        if (ret != 0) {
            ASSERT(false);
            format_context->result = -1;
            return -1;
        }

        ptr = buffer->data + buffer->len;
        len = buffer->cap - buffer->len;
        int32_t expect = (format->format)(format, msg, &ptr, &len);
        ASSERT(expect == 0);

        buffer->len = buffer->cap - len;
        format_context->result = 0;
        return 0;
    }

    ASSERT(false);
    format_context->result = -1;
    return -1;
}

LPEXTERN int lp_log_format(struct lp_buffer_t* buffer, struct lp_formatlist_t* formats, void* msg) {
    struct lp_format_context_t context = { buffer, msg, 0 };
    lp_formatlist_foreach(formats, &context, lp_formatlist_format);
    return context.result = 0;
}
